package client

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"go.uber.org/zap"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"
	"virus-proxy/internal/logger"
	"virus-proxy/internal/mount"
)

type QAX struct {
}

func NewQAX() *QAX {
	return &QAX{}
}

func (p *QAX) CallKillTask(scanFile string, clientIp string) ([]byte, error) {
	_ = mount.UpdateMount()
	// scanFile /test/a/b/c
	parts := strings.SplitN(scanFile, "/", 3)
	fsPath := bytes.NewBufferString("//" + clientIp + "/" + parts[1]).String()
	var mountPoint string
	var ok bool
	mount.MountSliceIns.Mutex.RLock()
	if mountPoint, ok = mount.MountSliceIns.DriversMap[fsPath]; !ok {
		mount.MountSliceIns.Mutex.RUnlock()
		return []byte(fmt.Sprintf("%s not mount", fsPath)), errors.New("not mount")
	}
	mount.MountSliceIns.Mutex.RUnlock()
	scanFile = filepath.Join(mountPoint, parts[2])
	if _, err := os.Stat(scanFile); err != nil {
		logger.LogError("scanFile not exist", zap.String("scanFile", scanFile))
	}
	//ex, _ := os.Executable()
	//program := filepath.Join(filepath.Dir(ex), "QiAnXin")
	escapeList := []string{" ", "(", ")", "{", "}", "[", "]", "$", ";", "'", "\"", "|", "<", ">", "#", "`", "!", "^", "&", "="}
	for _, s := range escapeList {
		scanFile = strings.ReplaceAll(scanFile, s, `\`+s)
	}
	ctx, cancel := context.WithTimeout(context.Background(), 6000*time.Second)
	defer cancel()
	cmd := exec.CommandContext(ctx, "bash", "-c", fmt.Sprintf("./QiAnXin %s", scanFile))
	//output, err := exec.Command(program, scanFile).CombinedOutput()
	//fmt.Printf("%T:%s %s %v\n", scanFile, scanFile, output, err)
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	var output bytes.Buffer
	cmd.Stdout = &output
	cmd.Stderr = &output
	if err := cmd.Start(); err != nil {
		logger.LogError("start QiAnXin failed", zap.String("file", scanFile), zap.Error(err))
		return nil, err
	}
	done := make(chan error, 1)
	go func() { done <- cmd.Wait() }()
	var err error
	select {
	case <-ctx.Done():
		syscall.Kill(cmd.Process.Pid, syscall.SIGKILL)
		return output.Bytes(), ctx.Err()
	case err = <-done:
		if err != nil {
			logger.LogError("scanFile failed", zap.String("file", scanFile), zap.String("result", output.String()), zap.Error(err))
		} else {
			logger.LogInfo("scanFile success", zap.String("file", scanFile), zap.String("result", output.String()))
		}
	}
	return output.Bytes(), err
}
